package com.sheepone.util.algorithm.analysis;

import com.sheepone.util.exception.ExceptionUtil;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.stream.DoubleStream;

/**
 * @author Miss.杨
 * @description
 * @since 2024/2/25 - 21:28
 */
public class DoubleAnalysisUtil {
    public static double DOUBLE_MIN_VALUE = -Double.MAX_VALUE;

    public static double ZERO = 0.0;

    /**
     * 判断数组中是否存在元素小于0
     * @param arr 数组
     * @return 是否存在元素小于0
     */
    public static boolean isAnyLessThanZero(double... arr) {
        for (double d : arr) {
            if (d < 0) {
                return true;
            }
        }
        return false;
    }



    /**
     * 交换数组中的两个元素
     *
     * @param arr
     * @param i
     * @param j
     */
    public static void swapInArray(double[] arr, int i, int j) {
        if (i != j && i < arr.length && j < arr.length) {
            double temp;
            temp = arr[i];
            arr[i] = arr[j];
            arr[j] = temp;
        }
    }

    /**
     * 求多个数的平均值
     *
     * @param arr
     * @return
     */
    public static double mean(double... arr) {
        // 将数组转换为Double Stream
        DoubleStream stream = Arrays.stream(arr);

        // 计算并输出平均值
        return stream.average().orElse(Double.NaN);
    }

    /**
     * 求多个Double数的平均值
     *
     * @param numbers
     * @return
     */
    public static double mean(Double[] numbers) {
        int length = numbers.length;
        if (length == 0) {
            return 0.0;
        }
        if (length == 1) {
            return numbers[0];
        }
        double sum = 0.0;
        for (Double number : numbers) {
            sum += number;
        }
        return sum / length;
    }

    /**
     * 求多个Double数的平均值
     *
     * @param numbers
     * @return
     */
    public static double mean(List<Double> numbers) {
        int length = numbers.size();
        if (length == 0) {
            return 0.0;
        }
        if (length == 1) {
            return numbers.get(0);
        }
        double sum = 0.0;
        for (Double number : numbers) {
            sum += number;
        }
        return sum / length;
    }

    /**
     * 求多个数的中位数
     *
     * @param numbers
     * @return
     */
    public static double median(double... numbers) {
        int length = numbers.length;
        if (length == 0) {
            return 0.0;
        }
        if (length == 1) {
            return numbers[0];
        }
        Arrays.sort(numbers);
        if ((length & 1) != 0) {
            return numbers[length / 2];
        }
        return (numbers[length / 2 - 1] + numbers[length / 2]) / 2;
    }

    /**
     * 求多个数的中位数,如果是偶数的话取中间数两个数的平均数
     *
     * @param numbers
     * @return
     */
    public static double median(Double[] numbers) {
        int length = numbers.length;
        if (length == 0) {
            return 0.0;
        }
        if (length == 1) {
            return numbers[0];
        }
        Arrays.sort(numbers);
        if ((length & 1) != 0) {
            return numbers[length / 2];
        }
        return (numbers[length / 2 - 1] + numbers[length / 2]) / 2;
    }

    /**
     * 求多个Double数的中位数
     *
     * @param numbers
     * @return
     */
    public static double median(List<Double> numbers) {
        int length = numbers.size();
        if (length == 0) {
            return 0.0;
        }
        if (length == 1) {
            return numbers.get(0);
        }
        Collections.sort(numbers);
        if ((length & 1) != 0) {
            return numbers.get(length / 2);
        }
        return (numbers.get(length / 2 - 1) + numbers.get(length / 2)) / 2.0;
    }

    /**
     * 求多个数的中位数,如果是偶数的话取中间数的最大值
     *
     * @param numbers
     * @return
     */
    public static double medianMax(double... numbers) {
        int length = numbers.length;
        if (length == 0) {
            return 0.0;
        }
        if (length == 1) {
            return numbers[0];
        }
        Arrays.sort(numbers);
        return numbers[length / 2];
    }

    /**
     * 求多个数的中位数,如果是偶数的话取中间数的最大值
     *
     * @param numbers
     * @return
     */
    public static double medianMax(Double[] numbers) {
        int length = numbers.length;
        if (length == 0) {
            return 0.0;
        }
        if (length == 1) {
            return numbers[0];
        }
        Arrays.sort(numbers);
        return numbers[length / 2];
    }

    /**
     * 求多个数的中位数,如果是偶数的话取中间数的最小值
     *
     * @param numbers
     * @return
     */
    public static double medianMin(double... numbers) {
        int length = numbers.length;
        if (length == 0) {
            return 0.0;
        }
        if (length == 1) {
            return numbers[0];
        }
        Arrays.sort(numbers);
        if ((length & 1) != 0) {
            return numbers[length / 2];
        }
        return numbers[length / 2 - 1];
    }

    /**
     * 求多个数的中位数,如果是偶数的话取中间数的最小值
     *
     * @param numbers
     * @return
     */
    public static double medianMin(Double[] numbers) {
        int length = numbers.length;
        if (length == 0) {
            return 0.0;
        }
        if (length == 1) {
            return numbers[0];
        }
        Arrays.sort(numbers);
        if ((length & 1) != 0) {
            return numbers[length / 2];
        }
        return numbers[length / 2 - 1];
    }

    /**
     * 求多个数的最大值的索引
     *
     * @param numbers
     * @return
     */
    public static int maxValueIndex(double[] numbers) {
        int maxIndex = 0;
        for (int i = 1; i < numbers.length; i++) {
            if (numbers[i] > numbers[maxIndex]) {
                maxIndex = i;
            }
        }
        return maxIndex;
    }

    /**
     * 求多个数的最大值
     *
     * @param numbers
     * @return
     */
    public static double max(double... numbers) {
        double max = DOUBLE_MIN_VALUE;
        for (Double number : numbers) {
            if (number > max) {
                max = number;
            }
        }
        return max;
    }

    /**
     * 求多个数的最大值
     *
     * @param numbers
     * @return
     */
    public static double max(Double[] numbers) {
        double max = DOUBLE_MIN_VALUE;
        for (Double number : numbers) {
            if (number > max) {
                max = number;
            }
        }
        return max;
    }

    /**
     * 求多个数的最大值
     *
     * @param numbers
     * @return
     */
    public static double max(List<Double> numbers) {
        double max = DOUBLE_MIN_VALUE;
        for (Double number : numbers) {
            if (number > max) {
                max = number;
            }
        }
        return max;
    }

    /**
     * 求多个数的最小值
     *
     * @param numbers
     * @return
     */
    public static double min(Double[] numbers) {
        double min = Double.MAX_VALUE;
        for (Double number : numbers) {
            if (number < min) {
                min = number;
            }
        }
        return min;
    }

    /**
     * 求多个数的最小值
     *
     * @param numbers
     * @return
     */
    public static double min(double... numbers) {
        double min = Double.MAX_VALUE;
        for (Double number : numbers) {
            if (number < min) {
                min = number;
            }
        }
        return min;
    }

    /**
     * 求多个数的最小值
     *
     * @param numbers
     * @return
     */
    public static double min(List<Double> numbers) {
        double min = Double.MAX_VALUE;
        for (Double number : numbers) {
            if (number < min) {
                min = number;
            }
        }
        return min;
    }

    /**
     * 是否为单调序列
     *
     * @param arr
     * @return
     */
    public static boolean isMonotonic(double[] arr) {
        boolean isIncreasing = true;
        int length = arr.length;
        for (int i = 1; i < length; i++) {
            if (arr[i] < arr[i - 1]) {
                isIncreasing = false;
                break;
            }
        }
        boolean isDecreasing = true;
        for (int i = 1; i < length; i++) {
            if (arr[i] > arr[i - 1]) {
                isDecreasing = false;
                break;
            }
        }
        return isIncreasing || isDecreasing;
    }

    /**
     * 前k个高频元素
     *
     * @param data
     * @param k
     * @return
     * @time-complexity n
     */
    public static double[] topKFrequent(double[] data, int k) {
        double[] ans = null;
        int cnt = 0;
        HashMap<Double, Integer> map = new HashMap<>();
        for (double num : data) {
            if (map.containsKey(num)) {
                map.put(num, map.get(num) + 1);
            } else {
                map.put(num, 1);
            }
        }
        if (map.size() < k) {
            ans = new double[map.size()];
            for (Double i : map.keySet()) {
                ans[cnt++] = i;
            }
            return ans;
        }
        List<Double>[] lists = new List[data.length + 1];
        for (Map.Entry<Double, Integer> entry : map.entrySet()) {
            Integer value = entry.getValue();
            if (lists[value] == null) {
                lists[value] = new ArrayList<>();
            }
            lists[value].add(entry.getKey());
        }
        ans = new double[k];
        for (int i = lists.length - 1; i >= 0 && cnt < k; i--) {
            if (lists[i] == null) {
                continue;
            }
            for (Double db : lists[i]) {
                ans[cnt++] = db;
            }
        }
        return ans;
    }

    /**
     * 生成均匀分布的Double数组
     * [start,end]
     *
     * @param start 起始值
     * @param end   结束值
     * @param step  步长
     * @return 等差数列
     */
    public static double[] newUniformDoubleArray(double start, double end, double step) {
        ExceptionUtil.throwIllegalArgumentExceptionIf(start > end, "start must not greater than end");
        int length = (int) Math.ceil((end - start) / step);
        double[] doubles = new double[length + 1];
        doubles[0] = start;
        for (int i = 1; i < length; i++) {
            doubles[i] = doubles[i - 1] + step;
        }
        doubles[length] = end;
        return doubles;
    }

    /**
     * 生成均匀分布的Double数组
     * [firstValue,firstValue+step*(size-1)]
     *
     * @param firstValue 起始值
     * @param size       数组大小
     * @param step       步长
     * @return 等差数列
     */

    public static double[] newUniformDoubleArray(double firstValue, int size, double step) {
        double[] doubles = new double[size];
        doubles[0] = firstValue;
        for (int i = 1; i < size; i++) {
            doubles[i] = doubles[i - 1] + step;
        }
        return doubles;
    }

    public static double sum(Double[] array) {
        if (array == null || array.length == 0) {
            return 0.0;
        }
        double sum = 0.0;
        for (Double v : array) {
            sum += v;
        }
        return sum;
    }

    public static double sum(double[] array) {
        if (array == null || array.length == 0) {
            return 0.0;
        }
        double sum = 0.0;
        for (double v : array) {
            sum += v;
        }
        return sum;
    }

    public static double sum(List<Double> array) {
        if (array == null || array.isEmpty()) {
            return 0.0;
        }
        double sum = 0.0;
        for (Double v : array) {
            sum += v;
        }
        return sum;
    }

    public static double sumPositive(Double[] array) {
        if (array == null || array.length == 0) {
            return 0.0;
        }
        double sum = 0.0;
        for (Double v : array) {
            if (v != null && v > 0) {
                sum += v;
            }
        }
        return sum;
    }

    public static double sumPositive(double... array) {
        if (array == null || array.length == 0) {
            return 0.0;
        }
        double sum = 0.0;
        for (Double v : array) {
            if (v > 0) {
                sum += v;
            }
        }
        return sum;
    }

    public static double range(double[] array) {
        if (array == null || array.length == 0) {
            return 0;
        }
        double min = array[0];
        double max = array[0];
        for (int i = 1; i < array.length; i++) {
            if (array[i] < min) {
                min = array[i];
            }
            if (array[i] > max) {
                max = array[i];
            }
        }
        return max - min;
    }

    public static double range(List<Double> array) {
        if (array == null || array.isEmpty()) {
            return 0;
        }
        double min = array.get(0);
        double max = array.get(0);
        for (int i = 1; i < array.size(); i++) {
            if (array.get(i) < min) {
                min = array.get(i);
            }
            if (array.get(i) > max) {
                max = array.get(i);
            }
        }
        return max - min;
    }

    /**
     * diff 函数是用于计算向量或矩阵中元素的差分
     * [a, b, c, d, e] -> [b-a, c-b, d-c, e-d]
     *
     * @param array
     * @return
     */
    public static List<Double> diff(List<Double> array) {
        if (array == null || array.size() < 2) {
            return null;
        }
        List<Double> doubles = new ArrayList<>();
        for (int i = 0; i < array.size() - 1; i++) {
            doubles.add(array.get(i + 1) - array.get(i));
        }
        return doubles;
    }

    /**
     * diff 函数是用于计算向量或矩阵中元素的差分
     * [a, b, c, d, e] -> [b-a, c-b, d-c, e-d]
     *
     * @param array
     * @return
     */
    public static double[] diff(Double[] array) {
        if (array == null || array.length < 2) {
            return null;
        }
        int size = array.length - 1;
        double[] doubles = new double[size];
        for (int i = 0; i < size; i++) {
            doubles[i] = array[i + 1] - array[i];
        }
        return doubles;
    }

    /**
     * diff 函数是用于计算向量或矩阵中元素的差分
     * [a, b, c, d, e] -> [b-a, c-b, d-c, e-d]
     *
     * @param array
     * @return
     */
    public static double[] diff(double[] array) {
        if (array == null || array.length < 2) {
            return null;
        }
        double[] doubles = new double[array.length - 1];
        for (int i = 0; i < array.length - 1; i++) {
            doubles[i] = array[i + 1] - array[i];
        }
        return doubles;
    }

    public static Double[] excludeValue(double value, Double[] array) {
        ArrayList<Double> list = new ArrayList<>();
        for (Double d : array) {
            if (d != value) {
                list.add(d);
            }
        }
        return list.toArray(new Double[0]);
    }

    public static void moreThanValue(double value, Double[] array) {
        for (int i = 0; i < array.length; i++) {
            if (array[i] < value) {
                array[i] = value;
            }
        }
    }

    /**
     * 保留scale位小数
     *
     * @param value
     * @param scale
     * @return
     */
    public static double scaleWithHalfUp(double value, int scale) {
        return new BigDecimal(value).setScale(scale, RoundingMode.HALF_UP).doubleValue();
    }

    public static double[] columnMinValue(double[][] values) {
        int length = values[0].length;
        double[] mins = new double[length];
        for (int i = 0; i < length; i++) {
            double min = values[0][i];
            for (int j = 1; j < values.length; j++) {
                if (values[j][i] < min) {
                    min = values[j][i];
                }
            }
            mins[i] = min;
        }
        return mins;
    }

    public static double[] columnMaxValue(double[][] values) {
        int length = values[0].length;
        double[] maxs = new double[length];
        for (int i = 0; i < length; i++) {
            double max = values[0][i];
            for (int j = 1; j < values.length; j++) {
                if (values[j][i] > max) {
                    max = values[j][i];
                }
            }
            maxs[i] = max;
        }
        return maxs;
    }
}
